home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 17 code / Window Zooming / ZoomCode.c
Encoding:
C/C++ Source or Header  |  1995-03-11  |  11.9 KB  |  322 lines  |  [TEXT/MPS ]

  1. #include <Types.h>
  2. #include <Menus.h>
  3. #include <Quickdraw.h>
  4. #include <Windows.h>
  5.  
  6. /*
  7.     12/07/94 modified by James E. Trudeau
  8.     Now uses a UPP for call to DeviceLoop()
  9. */
  10.  
  11. struct ZoomData {
  12.     GDHandle        screenWithLargestPartOfWindow;
  13.     unsigned long    largestArea;
  14.     Rect            windowBounds;
  15. };
  16. typedef struct ZoomData ZoomData, *ZoomDataPtr;
  17.  
  18. typedef void (*CalcIdealDocumentSizeProcPtr)(WindowPtr theWindow, Rect *idealContentSize);
  19.  
  20. enum {
  21.     kNudgeSlop    =    4,
  22.     kIconSpace    =    64
  23. };
  24.  
  25. void ZoomTheWindow(WindowPeek theWindow, short zoomState,
  26.                     CalcIdealDocumentSizeProcPtr calcRoutine);
  27. pascal void CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice,
  28.                                     long userData);
  29. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint,
  30.                             short idealOnScreenStartPoint, short idealOnScreenEndPoint,
  31.                             short screenEdge1, short screenEdge2);
  32.  
  33. static RgnHandle GetWindowContentRegion(WindowPeek theWindow);
  34. static RgnHandle GetWindowStructureRegion(WindowPeek theWindow);
  35. static void GetWindowPortRect(WindowPeek theWindow, Rect *portRect);
  36. static void SetWindowStandardState(WindowPeek theWindow, const Rect *standardState);
  37. static void GetWindowUserState(WindowPeek theWindow, Rect *userState);
  38.  
  39.  
  40. void ZoomTheWindow(WindowPeek theWindow, short zoomState,
  41.                     CalcIdealDocumentSizeProcPtr calcRoutine)
  42. {
  43.     ZoomData    zoomData;
  44.     Rect        newStandardRect;
  45.     Rect        scratchRect;
  46.     Rect        screenRect;
  47.     Rect        portRect;
  48.     Rect        contentRegionBoundingBox;
  49.     Rect        structureRegionBoundingBox;
  50.     Rect        deviceLoopRect;
  51.     GrafPtr        currentPort;
  52.     RgnHandle    scratchRegion;
  53.     RgnHandle    contentRegion;
  54.     RgnHandle    structureRegion;
  55.     GDHandle    mainDevice;
  56.     short        horizontalAmountOffScreen;
  57.     short        verticalAmountOffScreen;
  58.     short        windowFrameTopSize;
  59.     short        windowFrameLeftSize;
  60.     short        windowFrameRightSize;
  61.     short        windowFrameBottomSize;
  62.     
  63.  
  64.     GetPort(¤tPort);
  65.     SetPort((WindowPtr) theWindow);
  66.     contentRegion = GetWindowContentRegion(theWindow);
  67.     structureRegion = GetWindowStructureRegion(theWindow);
  68.     GetWindowPortRect(theWindow, &portRect);
  69.     contentRegionBoundingBox = (**contentRegion).rgnBBox;
  70.     structureRegionBoundingBox = (**structureRegion).rgnBBox;
  71.     
  72.     // Determine the size of the window frame
  73.     windowFrameTopSize = contentRegionBoundingBox.top - 
  74.                                     structureRegionBoundingBox.top;
  75.     windowFrameLeftSize = contentRegionBoundingBox.left - 
  76.                                     structureRegionBoundingBox.left;
  77.     windowFrameRightSize = structureRegionBoundingBox.right - 
  78.                                     contentRegionBoundingBox.right;
  79.     windowFrameBottomSize = structureRegionBoundingBox.bottom - 
  80.                                     contentRegionBoundingBox.bottom;
  81.                                     
  82.     // If the window is being zoomed into the standard state, calculate the best size
  83.     // to display the window’s information.
  84.     mainDevice = GetMainDevice();
  85.     if (zoomState == inZoomOut) {
  86.         zoomData.screenWithLargestPartOfWindow = mainDevice;
  87.         zoomData.largestArea = 0;
  88.     
  89.         // Usually, we would use the content region’s bounding box to determine the monitor
  90.         // with largest portion of the window’s area. However, if the entire content region
  91.         // of the window is not on any screen, the structure region should be used instead.
  92.         scratchRegion = NewRgn();
  93.         SectRgn(GetGrayRgn(), contentRegion, scratchRegion);
  94.         if (EmptyRgn(scratchRegion))
  95.             zoomData.windowBounds = structureRegionBoundingBox;
  96.         else
  97.             zoomData.windowBounds = contentRegionBoundingBox;
  98.     
  99.         // Use DeviceLoop to walk through all the active screens to find the one with the
  100.         // largest portion of the zoomed window
  101.         deviceLoopRect = zoomData.windowBounds;
  102.         GlobalToLocal((Point *)&deviceLoopRect);
  103.         GlobalToLocal((Point *)&deviceLoopRect.bottom);
  104.         RectRgn(scratchRegion, &deviceLoopRect);
  105.  
  106. /* jt 12/7/94 for PPC, replace original call to DeviceLoop to use UPP  */
  107. /*        DeviceLoop(scratchRegion, &CalcWindowAreaOnScreen, (long) &zoomData, */
  108. /*                    (DeviceLoopFlags) singleDevices); */
  109.  
  110.         {
  111.             DeviceLoopDrawingUPP myUPP =  NewDeviceLoopDrawingProc(CalcWindowAreaOnScreen);
  112.     
  113.             DeviceLoop(scratchRegion, &CalcWindowAreaOnScreen, (long) &zoomData,
  114.                     (DeviceLoopFlags) singleDevices);
  115.                     
  116.             DisposeRoutineDescriptor(myUPP);
  117.         }
  118.  
  119.  
  120.         DisposeRgn(scratchRegion);
  121.         screenRect = (**(zoomData.screenWithLargestPartOfWindow)).gdRect;
  122.         
  123.         // If the monitor being zoomed to is the main monitor, change the top of the
  124.         // useable screen area to avoid putting the title bar underneath the menubar.
  125.         if (zoomData.screenWithLargestPartOfWindow == mainDevice)
  126.             screenRect.top += GetMBarHeight();
  127.             
  128.         // Go figure out the perfect size for the window as if we had an infinitely large
  129.         // screen
  130.         (*calcRoutine)((WindowPtr) theWindow, &newStandardRect);
  131.         
  132.         // Anchor the new rectangle at the window’s current top left corner
  133.         OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
  134.         OffsetRect(&newStandardRect, contentRegionBoundingBox.left,
  135.                     contentRegionBoundingBox.top);
  136.         
  137.         // newStandardRect is the ideal size for the content area. The window frame
  138.         // needs to be accounted for when we see if the window needs to be moved,
  139.         // or resized, so add in the dimensions of the window frame.
  140.         newStandardRect.top -= windowFrameTopSize;
  141.         newStandardRect.left -= windowFrameLeftSize;
  142.         newStandardRect.right += windowFrameRightSize;
  143.         newStandardRect.bottom += windowFrameBottomSize;
  144.         
  145.         // If the new rectangle falls off the edge of the screen, nudge it so that it’s just
  146.         // on the screen. CalculateOffsetAmount determines how much of the window is offscreen.
  147.         SectRect(&newStandardRect, &screenRect, &scratchRect);
  148.         if (!EqualRect(&newStandardRect, &scratchRect)) {
  149.             horizontalAmountOffScreen = CalculateOffsetAmount(newStandardRect.left,
  150.                                                                newStandardRect.right,
  151.                                                                scratchRect.left,
  152.                                                                scratchRect.right,
  153.                                                                screenRect.left,
  154.                                                                screenRect.right);
  155.             verticalAmountOffScreen = CalculateOffsetAmount(newStandardRect.top,
  156.                                                             newStandardRect.bottom,
  157.                                                             scratchRect.top,
  158.                                                             scratchRect.bottom,
  159.                                                             screenRect.top,
  160.                                                             screenRect.bottom);
  161.             OffsetRect(&newStandardRect, horizontalAmountOffScreen,
  162.                         verticalAmountOffScreen);
  163.         }
  164.     
  165.         // If we’re still falling off the edge of the screen, that means that the perfect
  166.         // size is larger than the screen, so we need to shrink down the standard size
  167.         SectRect(&newStandardRect, &screenRect, &scratchRect);
  168.         if (!EqualRect(&newStandardRect, &scratchRect)) {
  169.  
  170.         // First shrink the width of the window. If the window is wider than the screen
  171.         // it is zooming to, we can just pin the standard rectangle to the edges of the
  172.         // screen, leaving some slop. If the window is narrower than the screen, we know
  173.         // we just nudged it into position, so nothing needs to be done.
  174.             if ((newStandardRect.right - newStandardRect.left) >
  175.                 (screenRect.right - screenRect.left)) {
  176.                 newStandardRect.left = screenRect.left + kNudgeSlop;
  177.                 newStandardRect.right = screenRect.right - kNudgeSlop;
  178.  
  179.                 if ((zoomData.screenWithLargestPartOfWindow == mainDevice) &&
  180.                     (newStandardRect.right > (screenRect.right - kIconSpace)))
  181.                     newStandardRect.right = screenRect.right - kIconSpace;
  182.             }
  183.  
  184.             // Move in the top. Like the width of the window, nothing needs to be done unless
  185.             // the window is taller than the height of the screen.
  186.             if ((newStandardRect.bottom - newStandardRect.top) >
  187.                 (screenRect.bottom - screenRect.top)) {
  188.                 newStandardRect.top = screenRect.top + kNudgeSlop;
  189.                 newStandardRect.bottom = screenRect.bottom - kNudgeSlop;
  190.             }
  191.         }
  192.  
  193.         // We’ve got the best possible window position. Remove the
  194.         // frame, slam it into the WStateData record and let ZoomWindow
  195.         // take care of the rest.
  196.         newStandardRect.top += windowFrameTopSize;
  197.         newStandardRect.left += windowFrameLeftSize;
  198.         newStandardRect.right -= windowFrameRightSize;
  199.         newStandardRect.bottom -= windowFrameBottomSize;
  200.         SetWindowStandardState(theWindow, &newStandardRect);
  201.     }
  202.     else
  203.         GetWindowUserState(theWindow, &newStandardRect);
  204.         
  205.     // If the window is still anchored at the current location, then just resize it
  206.     if ((newStandardRect.left == contentRegionBoundingBox.left) &&
  207.         (newStandardRect.top == contentRegionBoundingBox.top)) {
  208.         OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top);
  209.         SizeWindow((WindowPtr) theWindow, newStandardRect.right, newStandardRect.bottom,
  210.                     true);
  211.     }
  212.     else {
  213.         scratchRegion = NewRgn();
  214.         GetClip(scratchRegion);
  215.         ClipRect(&portRect);
  216.         EraseRect(&portRect);
  217.         ZoomWindow((WindowPtr) theWindow, zoomState, false);
  218.         SetClip(scratchRegion);
  219.         DisposeRgn(scratchRegion);
  220.     }
  221.     
  222.     SetPort(currentPort);
  223. }
  224.  
  225. pascal void    CalcWindowAreaOnScreen(short depth, short deviceFlags, GDHandle targetDevice, long userData)
  226. {
  227. #pragma unused (depth, deviceFlags)
  228.     ZoomDataPtr    zoomData = (ZoomDataPtr) userData;
  229.     long        windowAreaOnScreen;
  230.     Rect        windowPortionOnScreen;
  231.     
  232.     // Find the rectangle that encloses the intersection of the window and this screen.
  233.     SectRect(&(zoomData->windowBounds), &((**targetDevice).gdRect), &windowPortionOnScreen);
  234.     
  235.     // Offset the rectangle so that it’s right and bottom are also it’s width and height.
  236.     OffsetRect(&windowPortionOnScreen, -windowPortionOnScreen.left, -windowPortionOnScreen.top);
  237.     
  238.     // Calculate the area of the portion of the window that’s on this screen.
  239.     windowAreaOnScreen = (long) windowPortionOnScreen.right * (long) windowPortionOnScreen.bottom;
  240.     
  241.     // If this is the largest portion of the window that has been encountered so far,
  242.     // remember this screen as the potential screen to zoom to.
  243.     if (windowAreaOnScreen > zoomData->largestArea) {
  244.         zoomData->largestArea = windowAreaOnScreen;
  245.         zoomData->screenWithLargestPartOfWindow = targetDevice;
  246.     }
  247. }
  248.  
  249. // Figure out how much we need to move the window to get it entirely on the monitor.  If
  250. // the window wouldn’t fit completely on the monitor anyway, don’t move it at all; we’ll
  251. // make it fit later on.
  252.  
  253. short CalculateOffsetAmount(short idealStartPoint, short idealEndPoint, short idealOnScreenStartPoint,
  254.                             short idealOnScreenEndPoint, short screenEdge1, short screenEdge2)
  255. {
  256.     short    offsetAmount;
  257.  
  258.     // First check to see if the window fits on the screen in this dimension.
  259.     if ((idealStartPoint < screenEdge1) && (idealEndPoint > screenEdge2))
  260.         offsetAmount = 0;
  261.     else {
  262.     
  263.         // Find out how much of the window lies off this screen by subtracting the amount of the window
  264.         // that is on the screen from the size of the entire window in this dimension. If the window
  265.         // is completely offscreen, the offset amount is going to be the distance from the ideal
  266.         // starting point to the first edge of the screen.
  267.         if ((idealOnScreenStartPoint - idealOnScreenEndPoint) == 0) {
  268.             // See if the window is lying to the left or above the screen
  269.             if (idealEndPoint < screenEdge1)
  270.                 offsetAmount = screenEdge1 - idealStartPoint + kNudgeSlop;
  271.             else
  272.             // Otherwise, it’s below or to the right of the screen
  273.                 offsetAmount = screenEdge2 - idealEndPoint - kNudgeSlop;
  274.         }
  275.         else {
  276.             // Window is already partially or completely on the screen
  277.             offsetAmount = (idealEndPoint - idealStartPoint) -
  278.                             (idealOnScreenEndPoint - idealOnScreenStartPoint);
  279.     
  280.             // If we are offscreen a little, move the window in a few more pixels from the edge of the screen.
  281.             if (offsetAmount != 0)
  282.                 offsetAmount += kNudgeSlop;
  283.             
  284.             // Check to see which side of the screen the window was falling off of, so that it can be
  285.             // nudged in the opposite direction.
  286.             if (idealEndPoint > screenEdge2)
  287.                 offsetAmount = -offsetAmount;
  288.         }
  289.     }
  290.     
  291.     return offsetAmount;
  292. }
  293.  
  294. /*
  295.     WindowRecord accessor functions
  296. */
  297.  
  298. RgnHandle GetWindowContentRegion(WindowPeek theWindow)
  299. {
  300.     return (theWindow->contRgn);
  301. }
  302.  
  303. RgnHandle GetWindowStructureRegion(WindowPeek theWindow)
  304. {
  305.     return (theWindow->strucRgn);
  306. }
  307.  
  308. void GetWindowPortRect(WindowPeek theWindow, Rect *portRect)
  309. {
  310.     *portRect = theWindow->port.portRect;
  311. }
  312.  
  313. void SetWindowStandardState(WindowPeek theWindow, const Rect *standardState)
  314. {
  315.     (**((WStateDataHandle) theWindow->dataHandle)).stdState = *standardState;
  316. }
  317.  
  318. void GetWindowUserState(WindowPeek theWindow, Rect *userState)
  319. {
  320.     *userState = (**((WStateDataHandle) theWindow->dataHandle)).userState;
  321. }
  322.